Skip to content

fix: propagate tool exceptions to spans so StatusCode.ERROR is set correctly#2046

Open
mattdai01 wants to merge 1 commit intostrands-agents:mainfrom
mattdai01:fix/tool-span-error-status
Open

fix: propagate tool exceptions to spans so StatusCode.ERROR is set correctly#2046
mattdai01 wants to merge 1 commit intostrands-agents:mainfrom
mattdai01:fix/tool-span-error-status

Conversation

@mattdai01
Copy link
Copy Markdown

When a tool fails, the exception was being dropped before reaching end_tool_call_span, causing all tool spans to get StatusCode.OK even on errors. This threads the original exception through ToolResultEvent yields and into the tracer, and creates exceptions for cancel/unknown tool paths that previously had none.

Fixes #2016

Description

When a tool raises an exception, Strands catches it and converts it to a ToolResult dict with status='error'. The original exception object was dropped at the ToolResultEvent yield points in _executor.py, so end_tool_call_span never received it and always set StatusCode.OK. This breaks observability backends like Langfuse that rely on span status codes for error detection.

This PR fixes the root cause by:

  • Threading original exceptions through ToolResultEvent yields and into end_tool_call_span so tool spans correctly get StatusCode.ERROR
  • Creating exceptions for cancel and unknown-tool error paths that previously produced status: "error" results without an exception object

PR #2027 proposed synthesizing an Exception from error text in tracer.py. This PR instead fixes the root cause — the exception was already available but simply not threaded through. This preserves the original exception type and traceback.

Related Issues

Fixes #2016

Documentation PR

N/A

Type of Change

Bug fix

Testing

  • I ran hatch run prepare
  • Full test suite: 2488 passed, 0 failures
  • New tests:
    • test_end_tool_call_span_with_error — explicit error sets StatusCode.ERROR and record_exception
    • test_executor_stream_with_trace_error — exception flows from tool to tracer
    • test_executor_stream_error_preserves_exceptionToolResultEvent.exception is set
    • test_executor_stream_unknown_tool_has_exception — unknown tool path
    • test_executor_stream_cancel_has_exception — cancel path

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

…rrectly

When a tool fails, the exception was being dropped before reaching
end_tool_call_span, causing all tool spans to get StatusCode.OK even
on errors. This threads the original exception through ToolResultEvent
yields and into the tracer, and creates exceptions for cancel/unknown
tool paths that previously had none.

Fixes strands-agents#2016
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

Assessment: Approve

Clean, well-scoped bug fix that addresses the root cause rather than working around it. The approach of threading the original exception through ToolResultEvent and into the tracer preserves exception types and tracebacks, which is the right design. Test coverage is thorough with 5 new tests covering each error path (tracer unit, trace flow, preserved exception, unknown tool, cancel).

Review Details
  • Approach: Correctly fixes the root cause — the original exception was already available but not threaded through. This is preferable to the synthesized-exception approach in fix: set StatusCode.ERROR on tool spans when tool result status is error #2027.
  • Consistency: The cancel and unknown-tool paths now correctly create Exception objects for observability, following the same pattern as the decorator and exception handler paths.
  • Testing: All error paths are exercised, and existing test assertions were updated to validate the new exception / error parameters.
  • Scope: No public API changes, no documentation PR needed.

Nice fix — preserving the original exception chain is the right call for observability.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Strands Swallows Tool Call Errors

3 participants